package com.jumper.property.comparer.core.support;

import com.alibaba.fastjson.JSON;
import com.jumper.property.comparer.core.context.DataChangeContext;
import com.jumper.property.comparer.core.constant.LogDataConstant;
import com.jumper.property.comparer.core.enums.LogType;
import com.jumper.property.comparer.core.result.User;
import com.jumper.property.comparer.core.support.result.LogEntity;
import com.jumper.property.comparer.core.annotations.UniqueKey;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 批量操作日志管理器
 */
@Slf4j
public abstract class AbstractBatchLogManager<K extends UniqueKey<T>, T>
        extends AbstractLogManager<T>
        implements BatchOptLogManager<K, T>, OptDataBatchQueryService<K, T> {

    protected final ThreadLocal<Set<K>> beforeBatchLocal = new InheritableThreadLocal<>();
    protected final ThreadLocal<Set<K>> afterBatchLocal = new InheritableThreadLocal<>();

    public AbstractBatchLogManager(LogRepository repository) {
        super(repository);
    }

    @Override
    public void execute(Set<T> keys, String optName, Class<?> cls, User user, Consumer<InTransactionQuery> transactionFunc) {
        execute(keys, optName, cls, user, LogDataConstant.BATCH_SAVE_SIZE, transactionFunc);
    }

    @Override
    public void execute(Set<T> keys, String optName, Class<?> cls, User user, int batchSaveSize, Consumer<InTransactionQuery> transactionFunc) {
        submit(keys, optName, cls, user, () -> {
            transactionFunc.accept(
                    new BatchOptLogInTransactionQuery<>(keys, this)
            );
            return null;
        });
    }

    @Override
    public <V> V submit(Set<T> keys, String optName, Class<?> cls, User user, Function<InTransactionQuery, V> transactionFunc) {
        return submit(keys, optName, cls, user, LogDataConstant.BATCH_SAVE_SIZE, transactionFunc);
    }

    @Override
    public <V> V submit(Set<T> keys, String optName, Class<?> cls, User user, int batchSaveSize, Function<InTransactionQuery, V> transactionFunc) {
        V res;
        try {
            doBefore();
            beforeQuerySet(keys);
            res = transactionFunc.apply(
                    new BatchOptLogInTransactionQuery<>(keys, this)
            );
            changeCommit(keys, optName, cls, user, batchSaveSize);
        } finally {
            changeClear();
            doAfter();
        }
        return res;
    }

    @Override
    @Deprecated
    public void submit(Set<T> keys, String optName, Class<?> cls, User user, Runnable runnable) {
        submit(keys, optName, cls, user, LogDataConstant.BATCH_SAVE_SIZE, runnable);
    }

    @Override
    @Deprecated
    public void submit(Set<T> keys, String optName, Class<?> cls, User user, int batchSaveSize, Runnable runnable) {
        submit(keys, optName, cls, user, () -> {
            runnable.run();
            return null;
        });
    }

    @Override
    @Deprecated
    public <V> V submit(Set<T> keys, String optName, Class<?> cls, User user, Callback<V> callable) {
        return submit(keys, optName, cls, user, LogDataConstant.BATCH_SAVE_SIZE, callable);
    }

    @Override
    @Deprecated
    public <V> V submit(Set<T> keys, String optName, Class<?> cls, User user, int batchSaveSize, Callback<V> callable) {
        V res;
        try {
            doBefore();
            beforeQuerySet(keys);
            res = callable.call();
            changeCommit(keys, optName, cls, user, batchSaveSize);
        } finally {
            changeClear();
            doAfter();
        }
        return res;
    }

    /**
     * 清楚缓存
     */
    protected void changeClear() {
        beforeBatchLocal.remove();
        afterBatchLocal.remove();
    }

    /**
     * 数据变更之前
     */
    protected void beforeQuerySet(Set<T> keys) {
        beforeBatchLocal.set(getOptDataList(keys));
    }

    /**
     * 数据变更之前
     */
    protected void afterQuerySet(Set<T> keys) {
        afterBatchLocal.set(getOptDataList(keys));
    }

    /**
     * 数据变更提交
     */
    protected void changeCommit(Set<T> keys, String optName, Class<?> cls, User user, int batchSaveSize) {
        try {
            Set<K> before = beforeBatchLocal.get();
            Set<K> after = afterBatchLocal.get();
            if (after == null) {
                log.info("BatchAfter获取为空, 重新查询");
                after = getOptDataList(keys);
            }
            submit(before, after, optName, cls, user, batchSaveSize);
        } finally {
            changeClear();
        }
    }


    @Override
    @Deprecated
    public void submit(Set<K> beforeList, Set<K> afterList, String optName, Class<?> cls, User user) {
        submit(beforeList, afterList, optName, cls, user, LogDataConstant.BATCH_SAVE_SIZE);
    }

    @Override
    @Deprecated
    public void submit(Set<K> beforeList, Set<K> afterList, String optName, Class<?> cls, User user, int batchSaveSize) {
        async(() -> {
            Set<T> unionKeySet = mergeUnionKeySet(beforeList, afterList);
            Map<T, K> beforeMap = filterValidData(beforeList);
            Map<T, K> afterMap = filterValidData(afterList);

            List<LogEntity> logs = new ArrayList<>();
            unionKeySet.forEach(unionKey -> {
                K before = beforeMap.get(unionKey);
                K after = afterMap.get(unionKey);
                try {
                    DataChangeContext context = new DataChangeContext(String.valueOf(unionKey), optName, cls, before, after, user);
                    logs.addAll(convertToBizLog(logger.log(context, LogType.DataChange)));
                    if (logs.size() >= batchSaveSize) {
                        saveAndClear(logs);
                    }
                } catch (Exception e) {
                    log.error("批量保存操作业务变更日志异常,unionKey:{},optName:{},user:{},cls:{},before:{},after:{}",
                            unionKey, optName, JSON.toJSONString(user), cls, JSON.toJSONString(before), JSON.toJSONString(after), e);
                }
            });
            if (!logs.isEmpty()) {
                saveAndClear(logs);
            }
        });
    }

    /**
     * 保存并清除缓存
     */
    private void saveAndClear(List<LogEntity> logs) {
        flushToDB(logs);
        logs.clear();
    }

    /**
     * 过滤有效数据
     */
    private Map<T, K> filterValidData(Set<K> list) {
        return list.stream().filter(this::isNotNull).collect(Collectors.toMap(UniqueKey::getUniqueKey, Function.identity(), (x, y) -> x));
    }

    /**
     * 合并UnionKey
     */
    private Set<T> mergeUnionKeySet(Set<K> beforeDataList, Set<K> afterDataList) {
        return Stream.of(
                beforeDataList.stream().filter(this::isNotNull).map(UniqueKey::getUniqueKey).collect(Collectors.toSet()),
                afterDataList.stream().filter(this::isNotNull).map(UniqueKey::getUniqueKey).collect(Collectors.toSet())
        ).flatMap(Collection::stream).collect(Collectors.toSet());
    }

    /**
     * 非空判断
     */
    private boolean isNotNull(K e) {
        return Objects.nonNull(e.getUniqueKey()) && Objects.nonNull(e.getUniqueKey());
    }

    @Override
    public Object getOptData(T optDataKey) {
        throw new UnsupportedOperationException("此方法暂不支持");
    }
}
